Introduction
During the last 30 years, the modern epidemiology has been able to identify some important drawbacks of the classic epidemiologic methods. Causal Inference (Robins et al., 2000) and the Neyma-Rubin Potential Outcomes framework (Rubin, 2011) have provided the theory and statistical methods needed to identify recurrent problems in observational epidemologic research, such as:
- non collapsibility of the odds and hazard ratios,
- impact of paradoxical effects due to conditioning on colliders,
- left truncation,
- prevalent cases,
- selection bias related with the vague understanding of the effect of time on exposure and outcome and,
- effect of time dependent confounding and mediators.
- Etc.
To control for confounding, the classical epidemilogic methods require making the assumption that the effect measure is constant across levels of confounders included in the model.
Alternatively, James Robins in 1986 demonstrated that using standardization, implemented through the use of the G-formula, allowed to obtain unconfounded marginal estimation of the causal average treatment effect (ATE) under causal nontestable assumptions (Greenland and Robins, 1986). The most commonly used estimator for a binary treatment effect is the risk difference or ATE = \(\psi(P_{0})\).
The G-Formula
\[\psi(P_{0})\,=\,\sum_{w}\,\left[\sum_{y}\,P(Y=y\mid A=1,W=w)-\,\sum_{y}\,P(Y = y\mid A=0,W=w)\right]P(W=w)\]
where,
\[P(Y = y \mid A = a, W = w)\,=\,\frac{P(W = w, A = a, Y = y)}{\sum_{y}\,P(W = w, A = a, Y = y)}\]
is the conditional probability distribution of Y = y, given A = a, W = w and,
\[P(W = w)\,=\,\sum_{y,a}\,P(W = w, A = a, Y = y)\]
Classical epidemilogic methods require making the assumption that the effect measure is constant across levels of confounders included in the model. However, Standardization allows us to obtain an unconfounded summary effect measure without requiring this assumption. The G-formula is a generalization of standardization (Greenland and Robins, 1986).
The ATE can be estimated non-parametrically using the G-formula. However, the course of dimensionality in observational studies limits its estimation.
Hence, the estimation of the ATE using the G-formula relies mostly on parametric modelling assumptions and maximum likelihood estimation. The correct model specification in parametric modelling is crucial to obtain unbiased estimates of the true ATE (Rubin, 2011).
However, Mark van der Laan and collaborators have developed a double-robust estimation procedure to reduce bias against misspecification. The targeted maximum likelihood estimation (TMLE) is a semiparametric, efficient substitution estimator (Laan and Rose, 2011).
TMLE allows for data-adaptive estimation while obtaining valid statistical inferencebased on the targeted minimum loss-based estimation and machine learning algorithms to minimize the risk of model misspecification (Laan and Rose, 2011).
TMLE is a general algorithm for the construction of double-robust, semiparametric, efficient substitution estimators. TMLE allows for data-adaptive estimation while obtaining valid statistical inference.
TMLE implemtation uses the G-computation estimand (G-formula). Briefly, the TMLE algorithm uses information in the estimated exposure mechanism P(A|W) to update the initial estimator of the conditional expectaction of the outcome given the treatment and the set of covariates W, E\(_{0}\)(Y|A,W).
The targeted estimates are then substituted into the parameter mapping \(\Psi\). The updating step achieves a targeted bias reduction for the parameter of interest \(\psi(P_{0})\) (the true target parameter) and serves to solve the efficient score equation. As a result, TMLE is a double robust estimator.
TMLE it will be consistent for \(\psi(P_{0})\) if either the conditional expectation E\(_{0}\)(Y|A,W) or the exposure mechanism P\(_{0}\)(A|W) are estimated consistently. When both functions are consistently estimated, the TMLE will be efficient in that it achieves the lowest asymptotic variance among a large class of estimators. These asymptotic properties typically translate into lower bias and variance in finite samples (Bühlmann et al., 2016).
The general formula to estimate the ATE using the TMLE method:
\[\psi TMLE,n = \Psi(Q_{n}^{*})= {\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right)}. (1)\] The efficient influcence curve (IC) based on Hampel seminal paper (Hampel, 1974) is applied for statistical inference using TMLE:
\[IC_{n}(O_{i})\ \ =\ \left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{1}\left(A_{i},W_{i}\right)\right]+\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right) - \psi TMLE,n. (2)\] where the variance of the ATE:
\[\sigma({\psi_{0}})=\sqrt{\frac{Var(IC_{n})}{n}}. (3)\]
The procedure is available with standard software such as the tmle package in R (Gruber and Laan, 2011).
The advantages of TMLE have been repeatedly demonstrated in both simulation studies and applied analyses (Laan and Rose, 2011). Evidence shows that TMLE provides the less unbiased ATE estimate compared with other double-robust estimators (Neugebauer and Laan, 2005), (Laan and Rose, 2011) such as the combination of regression adjustment with inverse probability of treatment weighting (IPTW-RA) and the augmented inverse probability of treatment weighting (AIPTW). The AIPTW estimation is a two step procedure with two equations (propensity score equation and mean outcome equation).
To estimate the ATE using the AIPTW estimator one can set the estimation equation (EE) (4) equal to cero and use bootstrap to derive 95% confidence intervals (CI). However, solving the EE using the generalized method of moments (GMM), stacking both equations (propensity score and outcome), reduces the estimation and inference steps to only one. However, given that the propensity score in equation (4) can easily fall outside the range [0, 1] (if for some observations \(g_{n}(1|W_{i})\) is close to 1 or 0) the AIPTW estimation can be unstable (near violation of the positivity assumption). This represents the price of not being a substitution estimator as TMLE.
\[\psi_{0}^{AIPTW-ATE}\ \ =\ \frac{1}{n}\sum_{i=1}^{n}\left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{0}\left(A_{i},W_{i}\right)\right]+\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{0}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{0}\left(0,\ W_{i}\right). (4)\]
Structural causal framework
Direct Acyclic Graph
Figure 1. Direct Acyclic Graph
Causal assumptions
Under the counterfactual framework the following assumptions have to be considered to estimate the \(\psi(P_{0})\) (ATE) with a mondel for \(P_{0}\) augmented with additional nontestatble cuasal assumptions (Rubin, 2011), (Laan and Rose, 2011):
CMI or Randomization
(\(Y_{0},Y_{1}\perp\)A|W) of the binary treatment effect (A) on the outcome (Y) given the set of observed covariates (W), where W = (W1, W2, W3, … , Wk).
Positivity
a ϵ A: P(A=a | W) > 0
P(A=1|W=w) > 0 and P(A=0| W = w) > 0 for each possible w.
Consistency or SUTVA:
The observed outcome value, under the observed treatment, is equal to the counterfactual outcome corresponding to the observed treatment for identical independent distributed (i.i.d.) variables.
TMLE flow chart
Source: Mark van der Laan and Sherri Rose. Targeted learning: causal inference for observational and experimental dataSpringer Series in Statistics, 2011.
Figure 2. TMLE flow chart (Road map)
Data generation
Simulation
In R we create a function to generate the data. The function will have as input number of draws and as output the generated observed data (ObsData) including the counterfactuals (Y1, Y0).
The simulated data replicationg the DAG in Figure 1:
- Y: mortality binary indicator (1 death, 0 alive)
- A: binary treatment for emergency presentation at cancer diagnosis (1 EP, 0 NonEP)
- W1: Gender (1 male; 0 female)
- W2: Age at diagnosis (0 <65; 1 >=65)
- W3: Cancer TNM classification (scale from 1 to 4)
- W4: Comorbidities (scale from 1 to 5)
#install.packages("broom")
options(digits=4)
generateData <- function(n){
w1 <- rbinom(n, size=1, prob=0.5)
w2 <- rbinom(n, size=1, prob=0.65)
w3 <- round(runif(n, min=0, max=4), digits=3)
w4 <- round(runif(n, min=0, max=5), digits=3)
A <- rbinom(n, size=1, prob= plogis(-0.4 + 0.2*w2 + 0.15*w3 + 0.2*w4 + 0.15*w2*w4))
Y <- rbinom(n, size=1, prob= plogis(-1 + A -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
# counterfactual
Y.1 <- rbinom(n, size=1, prob= plogis(-1 + 1 -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
Y.0 <- rbinom(n, size=1, prob= plogis(-1 + 0 -0.1*w1 + 0.3*w2 + 0.25*w3 + 0.2*w4 + 0.15*w2*w4))
# return data.frame
data.frame(w1, w2, w3, w4, A, Y, Y.1, Y.0)
}
set.seed(7777)
ObsData <- generateData(n=10000)
True_Psi <- mean(ObsData$Y.1-ObsData$Y.0);
cat(" True_Psi:", True_Psi)
True_Psi: 0.198
Bias_Psi <- lm(data=ObsData, Y~ A)
cat("\n")
cat("\n Naive_Biased_Psi:",summary(Bias_Psi)$coef[2, 1])
Naive_Biased_Psi: 0.2631
Naive_Bias <- ((summary(Bias_Psi)$coef[2, 1])-True_Psi); cat("\n Naives bias:", Naive_Bias)
Naives bias: 0.06509
Naive_Relative_Bias <- (((summary(Bias_Psi)$coef[2, 1])-True_Psi)/True_Psi)*100; cat("\n Relative Naives bias:", Naive_Relative_Bias,"%")
Relative Naives bias: 32.88 %
Data visualization
# DT table = interactive
# install.packages("DT") # install DT first
library(DT)
datatable(head(ObsData, n = nrow(ObsData)), options = list(pageLength = 5, digits = 2))
TMLE simple implementation
Step 1: \(Q_{0}\)(A,W)
Estimation of the initial probability of the outcome (Y) given the treatment (A) and the set of covariates (W), denoted as the \(Q_{0}\)(A,W). To estimate \(Q_{0}\)(A,W) we can use a standard logistic regression model:
\[\text{logit}[P(Y=1|A,W)]\,=\,\beta_{0}\,+\,\beta_{1}A\,+\,\hat{\beta_{2}^{T}}W.\]
Therefore, we can estimate the initial probability as follows:
\[\bar{Q}^{0}(A,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{1}}A\,+\,\hat{\beta_{2}^{T}}W).\]
The predicted probability can be estimated using the Super Learner library implemented in the R package “Super-Learner” (Ref) to include any terms that are functions of A or W (e.g., polynomial terms of A and W, as well as the interaction terms of A and W, can be considered). Consequently, for each subject, the predicted probabilities for both potential outcomes \(\bar{Q}^{0}(0,W)\) and \(\bar{Q}^{0}(1,W)\) can be estimated by setting A = 0 and A = 1 for everyone respectively: \[\bar{Q}^{0}(0,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{2}^{T}}W),\] and,
\[\bar{Q}^{0}(1,W)\,=\,\text{expit}(\hat{\beta_{0}}\,+\,\hat{\beta_{1}}A\,+\,\hat{\beta_{2}^{T}}W).\]
ObsData <-subset(ObsData, select=c(w1,w2,w3,w4,A,Y))
Y <- ObsData$Y
A <- ObsData$A
w1 <- ObsData$w1
w2 <- ObsData$w2
w3 <- ObsData$w3
w4 <- ObsData$w4
m <- glm(Y ~ A + w1 + w2 + w3 + w4, family=binomial, data=ObsData)
Q <- cbind(QAW = predict(m),
Q1W = predict(m, newdata=data.frame(A = 1, w1, w2, w3, w4)),
Q0W = predict(m, newdata=data.frame(A = 0, w1, w2, w3, w4)))
Q0 <- as.data.frame(Q);mean(Q0$Q1W-Q0$Q0W)
[1] 1.026
Step 2: \(g_{0}(A,W)\)
Estimation of the probability of the treatment (A) given the set of covariates (W), denoted as \(g_{0}(A,W)\). We can use again a logistic regression model and to improve the prediction algorithm we can use the Super Learner library or any other machine learning estrategy:
\[\text{logit}[P(A=1|W)]\,=\,\beta_{0}\,+\,\beta_{1}^{T}W.\] Then, we estimate the predicted probability of P(A|W) = \(\hat{g}(1,W)\) using:
\[\hat{g}(1,W)\,=\,\text{expit}\,=\,(\hat{\beta_{0}}\,+\,\hat{\beta_{2}^{T}}W).\]
g <- glm(A ~ w2 + w3 + w4, family = binomial)
g1w = predict(g, type ="response");summary(g1w)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.358 0.594 0.681 0.671 0.759 0.875
Step 3: HAW and \(\epsilon\)
This step aims to find a better prediction model targeted at minimising mean squared error (MSE) for the potential outcomes by using the so-called efficient IC estimation equation. For the ATE on step convergence is guaranteed given \(\bar{Q}^{0}\) and \(\hat{g}(1,W)\) the fluctuating parameter is modelled using a parametric working model to estimate the fluctuation parameters \(\epsilon_{0}\) and \(\epsilon_{1}\) as follows:
\[\bar{Q^{1}}(A,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A, W)\right)\,+\,\hat{\epsilon_{0}}H_{0}(A,W)\,+\,\hat{\epsilon_{1}}H_{1}(A,W)\right] (5)\] \[\bar{Q^{1}}(0,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A,W)\right)\,+\,\hat{\epsilon_{0}}H_{0}(A,W)\right]\]
\[\bar{Q^{1}}(1,W)\,=\,\text{expit}\left[\text{logit}\left(\bar{Q^{0}}(A,W)\right)\,+\,\hat{\epsilon_{1}}H_{1}(A,W)\right]\] Where, \[H_{0}(A,W)\,=\,\frac{I(A=0)}{\hat{g}(0|W)}\;\text{and},\;H_{1}(A,W)\,=\,\frac{I(A=1)}{\hat{g}(1|W)}\] are referred to as clever covariates (note that \(\hat{g}(A|W)\) is estimted from step 2).
The fluctuation parameters \((\hat{\epsilon}_{0}\,,\,\hat{\epsilon}_{1})\) are estimated using maximum likelihood procedures by setting \(\text{logit}(\bar{Q^{0}}(A,W))\) as an offset in a intercept-free logistic regression with \(H_{0}\) and \(H_{1}\) as independent variables. Then, the estimated probability of the potential outcomes is updated by the substitution parameters \((\hat{\epsilon_{0}}\,,\,\hat{\epsilon_{1}})\). The substitution update is performed by setting A = 0 and A = 1 for each subject in the initial estimate probability of the potential outcomes \(\bar{Q^{1}}(0,W)\,,\,\bar{Q^{1}}(1,W)\), as well as in the clever covariates \(H_{0}(0,W)\;\text{and}\; H_{1}(1,W)\).
For the ATE, the updated estimate of the potential outcomes only needs one iteration \(\Psi(\bar{Q_{n}}^{*})\) from \(\bar{Q}^{0}(A,W)\,=>\bar{Q^{1}}(A,W)\). Therefore, model (5) targets \(E[\hat{Y}_{A=0}]\;\text{and}\; E[\hat{Y}_{A=1}]\) simultaneously by including both \(H_{0}(A,W)\,\text{and}\,H_{1}(A,W)\) in the model.
#Model 5: Clever covariate and fluctuating/substitution paramteres
h <- cbind(A/g1w -(1-A)/(1-g1w), 1/g1w, -1/(1-g1w))
epsilon <- coef(glm(Y ~ -1 + h[,1] + offset(Q[,"QAW"]), family = binomial));epsilon
h[, 1]
0.001189
Step 4: \(\bar{Q_{n}}^{*}\)
\[\psi TMLE,n = \Psi(Q_{n}^{*})= {\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right)}. (1)\]
Qstar <- plogis(Q + epsilon*h)
Psi <- mean(Qstar[,"Q1W"] - Qstar[,"Q0W"]);cat("TMLE_Psi:", Psi)
TMLE_Psi: 0.2004
cat("\n TMLE.SI_bias:", abs(True_Psi-Psi))
TMLE.SI_bias: 0.002383
cat("\n Relative_TMLE.SI_bias:",abs(True_Psi-Psi)/True_Psi*100,"%")
Relative_TMLE.SI_bias: 1.204 %
Step 5: Inference
\[IC_{n}(O_{i})\ \ =\ \left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{1}\left(A_{i},W_{i}\right)\right]+\bar{Q}_{n}^{1}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{1}\left(0,\ W_{i}\right) - \psi TMLE,n. (2)\]
where the variance of the ATE:
\[\sigma({\psi_{0}})=\sqrt{\frac{Var(IC_{n})}{n}}. (3)\]
Q <- as.data.frame(Q)
IC <- h[,1]*(Y-Q$QAW) + Q$Q1W - Q$Q0W - Psi;summary(IC)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.950 -0.855 -0.200 -0.006 0.570 14.300
n <- nrow(ObsData)
varHat.IC <- var(IC)/n; varHat.IC
[1] 0.0002024
#Psi and 95%CI for Psi
cat("\n TMLE.SI_bias:", abs(True_Psi-Psi))
TMLE.SI_bias: 0.002383
cat("\n 95%CI:", c(Psi-1.96*sqrt(varHat.IC), Psi+1.96*sqrt(varHat.IC)))
95%CI: 0.1725 0.2283
cat("\n Relative_TMLE.SI_bias:",abs(True_Psi-Psi)/True_Psi*100,"%")
Relative_TMLE.SI_bias: 1.204 %
Comparison with AIPTW
\[\psi_{0}^{AIPTW-ATE}\ \ =\ \frac{1}{n}\sum_{i=1}^{n}\left(\frac{I\left(A_{i}=1\right)}{g_n\left(1\left|W_{i}\right)\right)}\ -\ \frac{I\left(A_{i}=0\right)}{g_n\left(0\left|W_{i}\right)\right)}\ \right)\left[Y_{i}-\bar{Q}_{n}^{0}\left(A_{i},W_{i}\right)\right]+\frac{1}{n}\sum_{i=1}^{n}\bar{Q}_{n}^{0}\left(1,\ W_{i}\right)-\bar{Q}_{n}^{0}\left(0,\ W_{i}\right). (4)\]
AIPTW <- mean((h[,1]*(Y-Q$QAW))+(Q$Q1W-Q$Q0W)); AIPTW
[1] 0.1948
cat("\n AIPTW_bias:", abs(True_Psi-AIPTW))
AIPTW_bias: 0.003211
cat("\n Relative_AIPTW_bias:",abs(True_Psi-AIPTW)/True_Psi*100,"%")
Relative_AIPTW_bias: 1.622 %
TMLE using the Super-Learner
#Q0
library(SuperLearner)
Loading required package: nnls
Super Learner
Version: 2.0-19
Package created on 2016-02-02
#Specify SuperLearner libraries
SL.library <- c("SL.glm","SL.step","SL.glm.interaction")
#Data frame with X with baseline covariates and exposure
X <- subset(ObsData, select=c(A, w1, w2, w3, w4))
n <- nrow(ObsData)
#Create data frames with A=1 and A=0
X1<-X0<-X
X1$A <-1
X0$A <-0
#Create new data by stacking
newdata <- rbind(X,X1,X0)
#Call superlearner
Qinit <- SuperLearner(Y=ObsData$Y, X=X, newX=newdata, SL.library=SL.library, family="binomial")
Qinit
Call:
SuperLearner(Y = ObsData$Y, X = X, newX = newdata, family = "binomial",
SL.library = SL.library)
Risk Coef
SL.glm_All 0.1766 0.6002
SL.step_All 0.1766 0.0000
SL.glm.interaction_All 0.1767 0.3998
#Predictions
#Pred prob of survival given A, W
QbarAW <- Qinit$SL.predict[1:n]
#Pred prob of surv for each subject given A=1 and w
Qbar1W <- Qinit$SL.predict[(n+1):(2*n)]
#Pred prob of surv for each subject given A=0 and w
Qbar0W <- Qinit$SL.predict[(2*n+1):(3*n)]
#Simple substitution estimator Psi(Q0)
PsiHat.SS <- mean(Qbar1W-Qbar0W);PsiHat.SS
[1] 0.199
#Step 2 g_0(A|W) with SuperLearner
w <- subset(ObsData, select=c(w1,w2,w3,w4))
gHatSL <- SuperLearner(Y=ObsData$A, X=w, SL.library=SL.library, family = binomial)
gHatSL;mean(gHatSL)
Call:
SuperLearner(Y = ObsData$A, X = w, family = binomial, SL.library = SL.library)
Risk Coef
SL.glm_All 0.2091 0.0000
SL.step_All 0.2091 0.3803
SL.glm.interaction_All 0.2090 0.6197
argument is not numeric or logical: returning NA
[1] NA
#Generate the pred prob of A=1 and, A=0 given covariates
gHat1W <- gHatSL$SL.predict
gHat0W <- 1-gHat1W
#Step 3: Clever covariate
HAW <- as.numeric(ObsData$A==1)/gHat1W - as.numeric(ObsData$A==0)/gHat0W;mean(HAW)
[1] 0.002954
H1W <- 1/gHat1W
H0W <- -1/gHat0W
#Step 4: Substitution estimaiton Q* of the ATE.
logitUpdate <- glm(ObsData$Y ~ -1 + offset(qlogis(QbarAW))+HAW, family='binomial')
eps <- logitUpdate$coef;eps
HAW
0.0004483
#Calculating the predicted values for each subject under each txt
QbarAW.star <- plogis(qlogis(QbarAW)+eps*HAW)
Qbar1W.star <- plogis(qlogis(Qbar1W)+eps*H1W)
Qbar0W.star <- plogis(qlogis(Qbar0W)+eps*H0W)
PsiHat.TMLE.SL <- mean(Qbar1W.star) - mean(Qbar0W.star)
cat("PsiHat.TMLE.SL:", PsiHat.TMLE.SL)
PsiHat.TMLE.SL: 0.1995
cat("\n PsiHat.TMLE.SL_bias:", abs(True_Psi-PsiHat.TMLE.SL))
PsiHat.TMLE.SL_bias: 0.001456
cat("\n Relative_PsiHat.TMLE.SL_bias:",abs(True_Psi-PsiHat.TMLE.SL)/True_Psi*100,"%")
Relative_PsiHat.TMLE.SL_bias: 0.7354 %
R-TMLE
library(tmle)
Welcome to the tmle package, version 1.2.0-2
Use tmleNews() to see details on changes and bug fixes
Attaching package: ‘tmle’
The following object is masked from ‘package:SuperLearner’:
SL.glm.interaction
w <- subset(ObsData, select=c(w1,w2,w3,w4))
tmle <- tmle(Y, A, W=w)
cat("TMLER_Psi:", tmle$estimates[[2]][[1]],";","95%CI(", tmle$estimates[[2]][[3]],")")
TMLER_Psi: 0.1994 ; 95%CI( 0.1799 0.2189 )
cat("\n TMLE_bias:", abs(True_Psi-tmle$estimates[[2]][[1]]))
TMLE_bias: 0.001407
cat("\n Relative_TMLE_bias:",abs(True_Psi-tmle$estimates[[2]][[1]])/True_Psi*100,"%")
Relative_TMLE_bias: 0.7108 %
R-TMLE improving prediction
SL.TMLER.Psi <- tmle(Y=Y, A=A, W=w, family="binomial",
Q.SL.library = c("SL.glm", "SL.glm.interaction", "SL.gam", "SL.randomForest"),
g.SL.library = c("SL.glm", "SL.glm.interaction", "SL.gam", "SL.randomForest"))
cat("SL.TMLER.Psi:", SL.TMLER.Psi$estimates[[2]][[1]],";","95%CI(", SL.TMLER.Psi$estimates[[2]][[3]],")")
SL.TMLER.Psi: 0.1994 ; 95%CI( 0.1799 0.2188 )
cat("\n SL.TMLER.Psi_bias:", abs(True_Psi-SL.TMLER.Psi$estimates[[2]][[1]]))
SL.TMLER.Psi_bias: 0.001373
cat("\n Relative_SL.TMLER.Psi_bias:",abs(True_Psi-SL.TMLER.Psi$estimates[[2]][[1]])/True_Psi*100,"%")
Relative_SL.TMLER.Psi_bias: 0.6935 %
Thank you
Thank you for participating in this tutorial.
If you have updates or changes that you would like to make, please send me a pull request. Alternatively, if you have any questions, please e-mail me.
Miguel Angel Luque Fernandez
E-mail: miguel-angel.luque at lshtm.ac.uk
Twitter @WATZILEI
Session Info
devtools::session_info()
Session info --------------------------------------------------------------
setting value
version R version 3.3.0 (2016-05-03)
system x86_64, darwin13.4.0
ui RStudio (1.0.31)
language (EN)
collate en_US.UTF-8
tz Europe/London
date 2016-10-24
Packages ------------------------------------------------------------------
package * version date source
assertthat 0.1 2013-12-06 CRAN (R 3.3.0)
base64enc 0.1-3 2015-07-28 CRAN (R 3.3.0)
codetools 0.2-14 2015-07-15 CRAN (R 3.3.0)
devtools 1.12.0 2016-06-24 CRAN (R 3.3.0)
digest 0.6.10 2016-08-02 CRAN (R 3.3.0)
DT * 0.2 2016-08-09 CRAN (R 3.3.0)
evaluate 0.10 2016-10-11 CRAN (R 3.3.0)
foreach * 1.4.3 2015-10-13 CRAN (R 3.3.0)
formatR 1.4 2016-05-09 CRAN (R 3.3.0)
gam * 1.14 2016-09-10 CRAN (R 3.3.0)
htmltools 0.3.5 2016-03-21 CRAN (R 3.3.0)
htmlwidgets 0.7 2016-08-02 CRAN (R 3.3.0)
iterators 1.0.8 2015-10-13 CRAN (R 3.3.0)
jsonlite 1.1 2016-09-14 CRAN (R 3.3.0)
knitr 1.14 2016-08-13 CRAN (R 3.3.0)
magrittr 1.5 2014-11-22 CRAN (R 3.3.0)
memoise 1.0.0 2016-01-29 CRAN (R 3.3.0)
nnls * 1.4 2012-03-19 CRAN (R 3.3.0)
randomForest * 4.6-12 2015-10-07 CRAN (R 3.3.0)
Rcpp 0.12.7 2016-09-05 CRAN (R 3.3.0)
rmarkdown 1.1 2016-10-16 CRAN (R 3.3.0)
rsconnect 0.5 2016-10-17 CRAN (R 3.3.0)
stringi 1.1.2 2016-10-01 CRAN (R 3.3.0)
stringr 1.1.0 2016-08-19 CRAN (R 3.3.0)
SuperLearner * 2.0-19 2016-02-04 CRAN (R 3.3.0)
tibble 1.2 2016-08-26 CRAN (R 3.3.0)
tmle * 1.2.0-4 2014-03-09 CRAN (R 3.3.0)
withr 1.0.2 2016-06-20 CRAN (R 3.3.0)
yaml 2.1.13 2014-06-12 CRAN (R 3.3.0)
References
Bühlmann P, Drineas P, Laan M van der, Kane M. (2016). Handbook of big data. CRC Press.
Greenland S, Robins JM. (1986). Identifiability, exchangeability, and epidemiological confounding. International journal of epidemiology 15: 413–419.
Gruber S, Laan M van der. (2011). Tmle: An r package for targeted maximum likelihood estimation. UC Berkeley Division of Biostatistics Working Paper Series.
Hampel FR. (1974). The influence curve and its role in robust estimation. Journal of the American Statistical Association 69: 383–393.
Laan M van der, Rose S. (2011). Targeted learning: Causal inference for observational and experimental data. Springer Series in Statistics.
Neugebauer R, Laan M van der. (2005). Why prefer double robust estimators in causal inference? Journal of Statistical Planning and Inference 129: 405–426.
Robins JM, Hernan MA, Brumback B. (2000). Marginal structural models and causal inference in epidemiology. Epidemiology 550–560.
Rubin DB. (2011). Causal inference using potential outcomes. Journal of the American Statistical Association.
LS0tCnRpdGxlOiAiVE1MRSBzdGVwIGJ5IHN0ZXAiCmF1dGhvcjogJ0J5OiBNaWd1ZWwgQW5nZWwgTHVxdWUgRmVybmFuZGV6IG1pZ3VlbC1hbmdlbC5sdXF1ZUBsc2h0bS5hYy51aycKZGF0ZTogIk9jdG9iZXIgMjV0aCwgMjAxNiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGhpZ2hsaWdodDogZGVmYXVsdAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBqb3VybmFsCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgICB0b2NfZGVwdGg6IDMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwpmb250LWZhbWlseTogUmlzcXVlCmZvbnQtaW1wb3J0OiBodHRwOi8vZm9udHMuZ29vZ2xlYXBpcy5jb20vY3NzP2ZhbWlseT1SaXNxdWUKY3NsOiByZWZlcmVuY2VzL2lzbWUuY3NsCmJpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy9iaWJsaW9ncmFwaHkuYmliCi0tLQoKPCEtLUJFR0lOOiAgU2V0IHRoZSBnbG9iYWwgb3B0aW9ucyBhbmQgbG9hZCBwYWNrYWdlcy0tPgpgYGB7ciBzZXQtZ2xvYmFsLW9wdGlvbnMsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIGVjaG8gPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIGNhY2hlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBkZXBlbmRzb24gPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgZW5naW5lID0gIlIiLCAjIENodW5rcyB3aWxsIGFsd2F5cyBoYXZlIFIgY29kZSwgdW5sZXNzIG5vdGVkCiAgICAgICAgICAgICAgICAgICAgICBlcnJvciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcucGF0aD0iRmlndXJlcy8iLCAgIyBTZXQgdGhlIGZpZ3VyZSBvcHRpb25zCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSA3LAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDcpCiNZb3UgbmVlZCB0aGUgc3VnZ2VzdGVkIHBhY2thZ2VzIHRvIHJ1biB0aGlzIG5vdGVib29rCiNpbnN0YWxsLnBhY2thZ2VzKCd0bWxlJywgJ1N1cGVyTGVhcm5lcicsICdicm9vbScsICdEVCcpCnJlcXVpcmUoJ3RtbGUnLCAnU3VwZXJMZWFybmVyJywgJ2Jyb29tJywgJ0RUJykKYGBgCgojSW50cm9kdWN0aW9uCgpEdXJpbmcgdGhlIGxhc3QgMzAgeWVhcnMsIHRoZSBtb2Rlcm4gZXBpZGVtaW9sb2d5IGhhcyBiZWVuIGFibGUgdG8gaWRlbnRpZnkgc29tZSBpbXBvcnRhbnQgZHJhd2JhY2tzIG9mIHRoZSBjbGFzc2ljIGVwaWRlbWlvbG9naWMgbWV0aG9kcy4gQ2F1c2FsIEluZmVyZW5jZSBbQHJvYmluczIwMDBdIGFuZCB0aGUgTmV5bWEtUnViaW4gUG90ZW50aWFsIE91dGNvbWVzIGZyYW1ld29yayBbQHJ1YmluMjAxMV0gaGF2ZSBwcm92aWRlZCB0aGUgdGhlb3J5IGFuZCBzdGF0aXN0aWNhbCBtZXRob2RzIG5lZWRlZCB0byBpZGVudGlmeSByZWN1cnJlbnQgcHJvYmxlbXMgaW4gb2JzZXJ2YXRpb25hbCBlcGlkZW1vbG9naWMgcmVzZWFyY2gsIHN1Y2ggYXM6CgoxLiBub24gY29sbGFwc2liaWxpdHkgb2YgdGhlIG9kZHMgYW5kIGhhemFyZCByYXRpb3MsCjIuIGltcGFjdCBvZiBwYXJhZG94aWNhbCBlZmZlY3RzIGR1ZSB0byBjb25kaXRpb25pbmcgb24gY29sbGlkZXJzLAozLiBsZWZ0IHRydW5jYXRpb24sCjQuIHByZXZhbGVudCBjYXNlcywKNS4gc2VsZWN0aW9uIGJpYXMgcmVsYXRlZCB3aXRoIHRoZSB2YWd1ZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBlZmZlY3Qgb2YgdGltZSBvbiBleHBvc3VyZSBhbmQgb3V0Y29tZSBhbmQsCjYuIGVmZmVjdCBvZiB0aW1lIGRlcGVuZGVudCBjb25mb3VuZGluZyBhbmQgbWVkaWF0b3JzLgo3LiBFdGMuCgpUbyBjb250cm9sIGZvciBjb25mb3VuZGluZywgdGhlIGNsYXNzaWNhbCBlcGlkZW1pbG9naWMgbWV0aG9kcyByZXF1aXJlIG1ha2luZyB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBlZmZlY3QgbWVhc3VyZSBpcyBjb25zdGFudCBhY3Jvc3MgbGV2ZWxzIG9mIGNvbmZvdW5kZXJzIGluY2x1ZGVkIGluIHRoZSBtb2RlbC4gIAoKQWx0ZXJuYXRpdmVseSwgSmFtZXMgUm9iaW5zIGluIDE5ODYgZGVtb25zdHJhdGVkIHRoYXQgdXNpbmcgc3RhbmRhcmRpemF0aW9uLCBpbXBsZW1lbnRlZCB0aHJvdWdoIHRoZSB1c2Ugb2YgdGhlICoqRy1mb3JtdWxhKiosIGFsbG93ZWQgdG8gb2J0YWluIHVuY29uZm91bmRlZCBtYXJnaW5hbCBlc3RpbWF0aW9uIG9mIHRoZSBjYXVzYWwgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IChBVEUpIHVuZGVyIGNhdXNhbCBub250ZXN0YWJsZSBhc3N1bXB0aW9ucyBbQHJvYmluczE5ODZdLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIGVzdGltYXRvciBmb3IgYSBiaW5hcnkgdHJlYXRtZW50IGVmZmVjdCBpcyB0aGUgcmlzayBkaWZmZXJlbmNlIG9yICoqQVRFKiogPSAkXHBzaShQX3swfSkkLiAgICAgIAoKI1RoZSBHLUZvcm11bGEKCiQkXHBzaShQX3swfSlcLD1cLFxzdW1fe3d9XCxcbGVmdFtcc3VtX3t5fVwsUChZPXlcbWlkIEE9MSxXPXcpLVwsXHN1bV97eX1cLFAoWSA9IHlcbWlkIEE9MCxXPXcpXHJpZ2h0XVAoVz13KSQkICAKCndoZXJlLCAgIAoKJCRQKFkgPSB5IFxtaWQgQSA9IGEsIFcgPSB3KVwsPVwsXGZyYWN7UChXID0gdywgQSA9IGEsIFkgPSB5KX17XHN1bV97eX1cLFAoVyA9IHcsIEEgPSBhLCBZID0geSl9JCQgICAgIAppcyB0aGUgY29uZGl0aW9uYWwgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIG9mIFkgPSB5LCBnaXZlbiBBID0gYSwgVyA9IHcgYW5kLCAKCiQkUChXID0gdylcLD1cLFxzdW1fe3ksYX1cLFAoVyA9IHcsIEEgPSBhLCBZID0geSkkJCAKCiogQ2xhc3NpY2FsIGVwaWRlbWlsb2dpYyBtZXRob2RzIHJlcXVpcmUgbWFraW5nIHRoZSBhc3N1bXB0aW9uIHRoYXQgdGhlIGVmZmVjdCBtZWFzdXJlIGlzIGNvbnN0YW50IGFjcm9zcyBsZXZlbHMgb2YgY29uZm91bmRlcnMgaW5jbHVkZWQgaW4gdGhlIG1vZGVsLiBIb3dldmVyLCAqKlN0YW5kYXJkaXphdGlvbioqIGFsbG93cyB1cyB0byBvYnRhaW4gYW4gdW5jb25mb3VuZGVkIHN1bW1hcnkgZWZmZWN0IG1lYXN1cmUgd2l0aG91dCByZXF1aXJpbmcgdGhpcyBhc3N1bXB0aW9uLiBUaGUgKipHLWZvcm11bGEqKiBpcyBhICpnZW5lcmFsaXphdGlvbiBvZiBzdGFuZGFyZGl6YXRpb24qIFtAcm9iaW5zMTk4Nl0uICAgIAoKKiBUaGUgQVRFIGNhbiBiZSBlc3RpbWF0ZWQgKipub24tcGFyYW1ldHJpY2FsbHkqKiB1c2luZyB0aGUgRy1mb3JtdWxhLiBIb3dldmVyLCB0aGUgY291cnNlIG9mIGRpbWVuc2lvbmFsaXR5IGluIG9ic2VydmF0aW9uYWwgc3R1ZGllcyBsaW1pdHMgaXRzIGVzdGltYXRpb24uICAgCgoqIEhlbmNlLCB0aGUgZXN0aW1hdGlvbiBvZiB0aGUgQVRFIHVzaW5nIHRoZSBHLWZvcm11bGEgcmVsaWVzIG1vc3RseSBvbiAqKnBhcmFtZXRyaWMgbW9kZWxsaW5nKiogYXNzdW1wdGlvbnMgYW5kIG1heGltdW0gbGlrZWxpaG9vZCBlc3RpbWF0aW9uLiBUaGUgKipjb3JyZWN0IG1vZGVsIHNwZWNpZmljYXRpb24qKiBpbiBwYXJhbWV0cmljIG1vZGVsbGluZyBpcyBjcnVjaWFsIHRvIG9idGFpbiB1bmJpYXNlZCBlc3RpbWF0ZXMgb2YgdGhlIHRydWUgQVRFIFtAcnViaW4yMDExXS4gIAoKSG93ZXZlciwgTWFyayB2YW4gZGVyIExhYW4gYW5kIGNvbGxhYm9yYXRvcnMgaGF2ZSBkZXZlbG9wZWQgYSBkb3VibGUtcm9idXN0IGVzdGltYXRpb24gcHJvY2VkdXJlICoqdG8gcmVkdWNlIGJpYXMgYWdhaW5zdCBtaXNzcGVjaWZpY2F0aW9uKiouIFRoZSB0YXJnZXRlZCBtYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbiAoVE1MRSkgaXMgYSBzZW1pcGFyYW1ldHJpYywgZWZmaWNpZW50IHN1YnN0aXR1dGlvbiBlc3RpbWF0b3IgW0B2YW4yMDExXS4gICAgCgoqKlRNTEUqKiBhbGxvd3MgZm9yIGRhdGEtYWRhcHRpdmUgZXN0aW1hdGlvbiB3aGlsZSBvYnRhaW5pbmcgdmFsaWQgc3RhdGlzdGljYWwgaW5mZXJlbmNlYmFzZWQgb24gdGhlIHRhcmdldGVkIG1pbmltdW0gbG9zcy1iYXNlZCBlc3RpbWF0aW9uIGFuZCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgdG8gbWluaW1pemUgdGhlIHJpc2sgb2YgbW9kZWwgbWlzc3BlY2lmaWNhdGlvbiBbQHZhbjIwMTFdLiAgICAgIAoKMS4gKipUTUxFKiogaXMgYSBnZW5lcmFsIGFsZ29yaXRobSBmb3IgdGhlIGNvbnN0cnVjdGlvbiBvZiBkb3VibGUtcm9idXN0LCBzZW1pcGFyYW1ldHJpYywgZWZmaWNpZW50IHN1YnN0aXR1dGlvbiBlc3RpbWF0b3JzLiAqKlRNTEUqKiBhbGxvd3MgZm9yIGRhdGEtYWRhcHRpdmUgZXN0aW1hdGlvbiB3aGlsZSBvYnRhaW5pbmcgdmFsaWQgc3RhdGlzdGljYWwgaW5mZXJlbmNlLiAKCjIuICoqVE1MRSoqIGltcGxlbXRhdGlvbiB1c2VzIHRoZSBHLWNvbXB1dGF0aW9uIGVzdGltYW5kIChHLWZvcm11bGEpLiBCcmllZmx5LCB0aGUgKipUTUxFKiogYWxnb3JpdGhtIHVzZXMgaW5mb3JtYXRpb24gaW4gdGhlIGVzdGltYXRlZCBleHBvc3VyZSBtZWNoYW5pc20gUChBfFcpIHRvIHVwZGF0ZSB0aGUgaW5pdGlhbCBlc3RpbWF0b3Igb2YgdGhlIGNvbmRpdGlvbmFsIGV4cGVjdGFjdGlvbiBvZiB0aGUgb3V0Y29tZSBnaXZlbiB0aGUgdHJlYXRtZW50IGFuZCB0aGUgc2V0IG9mIGNvdmFyaWF0ZXMgVywgRSRfezB9JChZfEEsVykuIAoKMy4gVGhlIHRhcmdldGVkIGVzdGltYXRlcyBhcmUgdGhlbiBzdWJzdGl0dXRlZCBpbnRvIHRoZSBwYXJhbWV0ZXIgbWFwcGluZyAkXFBzaSQuIFRoZSB1cGRhdGluZyBzdGVwIGFjaGlldmVzIGEgdGFyZ2V0ZWQgYmlhcyByZWR1Y3Rpb24gZm9yIHRoZSBwYXJhbWV0ZXIgb2YgaW50ZXJlc3QgJFxwc2koUF97MH0pJCAodGhlIHRydWUgdGFyZ2V0IHBhcmFtZXRlcikgYW5kIHNlcnZlcyB0byBzb2x2ZSB0aGUgZWZmaWNpZW50IHNjb3JlIGVxdWF0aW9uLiBBcyBhIHJlc3VsdCwgKipUTUxFKiogaXMgYSBkb3VibGUgcm9idXN0IGVzdGltYXRvci4gCgo0LiAqKlRNTEUqKiBpdCB3aWxsIGJlIGNvbnNpc3RlbnQgZm9yICRccHNpKFBfezB9KSQgaWYgZWl0aGVyIHRoZSBjb25kaXRpb25hbCBleHBlY3RhdGlvbiBFJF97MH0kKFl8QSxXKSBvciB0aGUgZXhwb3N1cmUgbWVjaGFuaXNtIFAkX3swfSQoQXxXKSBhcmUgZXN0aW1hdGVkIGNvbnNpc3RlbnRseS4gV2hlbiBib3RoIGZ1bmN0aW9ucyBhcmUgY29uc2lzdGVudGx5IGVzdGltYXRlZCwgdGhlICoqVE1MRSoqIHdpbGwgYmUgZWZmaWNpZW50IGluIHRoYXQgaXQgYWNoaWV2ZXMgdGhlIGxvd2VzdCBhc3ltcHRvdGljIHZhcmlhbmNlIGFtb25nIGEgbGFyZ2UgY2xhc3Mgb2YgZXN0aW1hdG9ycy4gVGhlc2UgYXN5bXB0b3RpYyBwcm9wZXJ0aWVzIHR5cGljYWxseSB0cmFuc2xhdGUgaW50byBsb3dlciBiaWFzIGFuZCB2YXJpYW5jZSBpbiBmaW5pdGUgc2FtcGxlcyBbQGJ1aDIwMTZdLiAKClRoZSBnZW5lcmFsIGZvcm11bGEgdG8gZXN0aW1hdGUgdGhlIEFURSB1c2luZyB0aGUgVE1MRSBtZXRob2Q6ICAKCiQkXHBzaSBUTUxFLG4gPSBcUHNpKFFfe259XnsqfSk9IHtcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGJhcntRfV97bn1eezF9XGxlZnQoMSxcIFdfe2l9XHJpZ2h0KS1cYmFye1F9X3tufV57MX1cbGVmdCgwLFwgV197aX1ccmlnaHQpfS4gICgxKSQkClRoZSBlZmZpY2llbnQgaW5mbHVjZW5jZSBjdXJ2ZSAoSUMpIGJhc2VkIG9uIEhhbXBlbCBzZW1pbmFsIHBhcGVyIFtAaGFtcGVsMTk3NF0gaXMgYXBwbGllZCBmb3Igc3RhdGlzdGljYWwgaW5mZXJlbmNlIHVzaW5nIFRNTEU6CgokJElDX3tufShPX3tpfSlcIFwgPVwgXGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezF9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcYmFye1F9X3tufV57MX1cbGVmdCgxLFwgV197aX1ccmlnaHQpLVxiYXJ7UX1fe259XnsxfVxsZWZ0KDAsXCBXX3tpfVxyaWdodCkgLSBccHNpIFRNTEUsbi4gKDIpJCQKd2hlcmUgdGhlIHZhcmlhbmNlIG9mIHRoZSBBVEU6ICAKCiQkXHNpZ21hKHtccHNpX3swfX0pPVxzcXJ0e1xmcmFje1ZhcihJQ197bn0pfXtufX0uICAoMykkJAoKNS4gVGhlIHByb2NlZHVyZSBpcyBhdmFpbGFibGUgd2l0aCBzdGFuZGFyZCBzb2Z0d2FyZSBzdWNoIGFzIHRoZSAqKnRtbGUqKiBwYWNrYWdlIGluIFIgIFtAZ3J1YmVyMjAxMV0uIAoKNi4gVGhlIGFkdmFudGFnZXMgb2YgKipUTUxFKiogaGF2ZSBiZWVuIHJlcGVhdGVkbHkgZGVtb25zdHJhdGVkIGluIGJvdGggc2ltdWxhdGlvbiBzdHVkaWVzIGFuZCBhcHBsaWVkIGFuYWx5c2VzIFtAdmFuMjAxMV0uIEV2aWRlbmNlIHNob3dzIHRoYXQgKipUTUxFKiogcHJvdmlkZXMgdGhlIGxlc3MgdW5iaWFzZWQgQVRFIGVzdGltYXRlIGNvbXBhcmVkIHdpdGggb3RoZXIgZG91YmxlLXJvYnVzdCBlc3RpbWF0b3JzIFtAbmV1MjAwNV0sIFtAdmFuMjAxMV0gc3VjaCBhcyB0aGUgY29tYmluYXRpb24gb2YgcmVncmVzc2lvbiBhZGp1c3RtZW50IHdpdGggaW52ZXJzZSBwcm9iYWJpbGl0eSBvZiB0cmVhdG1lbnQgd2VpZ2h0aW5nIChJUFRXLVJBKSBhbmQgdGhlIGF1Z21lbnRlZCBpbnZlcnNlIHByb2JhYmlsaXR5IG9mIHRyZWF0bWVudCB3ZWlnaHRpbmcgKEFJUFRXKS4gVGhlIEFJUFRXIGVzdGltYXRpb24gaXMgYSB0d28gc3RlcCBwcm9jZWR1cmUgd2l0aCB0d28gZXF1YXRpb25zIChwcm9wZW5zaXR5IHNjb3JlIGVxdWF0aW9uIGFuZCBtZWFuIG91dGNvbWUgZXF1YXRpb24pLiAgCgo3LiBUbyBlc3RpbWF0ZSB0aGUgQVRFIHVzaW5nIHRoZSBBSVBUVyBlc3RpbWF0b3Igb25lIGNhbiBzZXQgdGhlIGVzdGltYXRpb24gZXF1YXRpb24gKEVFKSAoNCkgZXF1YWwgdG8gY2VybyBhbmQgdXNlIGJvb3RzdHJhcCB0byBkZXJpdmUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIChDSSkuIEhvd2V2ZXIsIHNvbHZpbmcgdGhlIEVFIHVzaW5nIHRoZSBnZW5lcmFsaXplZCBtZXRob2Qgb2YgbW9tZW50cyAoR01NKSwgc3RhY2tpbmcgYm90aCBlcXVhdGlvbnMgKHByb3BlbnNpdHkgc2NvcmUgYW5kIG91dGNvbWUpLCByZWR1Y2VzIHRoZSBlc3RpbWF0aW9uIGFuZCBpbmZlcmVuY2Ugc3RlcHMgdG8gb25seSBvbmUuIEhvd2V2ZXIsIGdpdmVuIHRoYXQgdGhlIHByb3BlbnNpdHkgc2NvcmUgaW4gZXF1YXRpb24gKDQpIGNhbiBlYXNpbHkgZmFsbCBvdXRzaWRlIHRoZSByYW5nZSBbMCwgMV0gKGlmIGZvciBzb21lIG9ic2VydmF0aW9ucyAkZ197bn0oMXxXX3tpfSkkIGlzIGNsb3NlIHRvIDEgb3IgMCkgdGhlIEFJUFRXIGVzdGltYXRpb24gY2FuIGJlIHVuc3RhYmxlIChuZWFyIHZpb2xhdGlvbiBvZiB0aGUgcG9zaXRpdml0eSBhc3N1bXB0aW9uKS4gVGhpcyByZXByZXNlbnRzIHRoZSBwcmljZSBvZiBub3QgYmVpbmcgYSBzdWJzdGl0dXRpb24gZXN0aW1hdG9yIGFzICoqVE1MRSoqLiAgICAgICAgCgokJFxwc2lfezB9XntBSVBUVy1BVEV9XCBcID1cIFxmcmFjezF9e259XHN1bV97aT0xfV57bn1cbGVmdChcZnJhY3tJXGxlZnQoQV97aX09MVxyaWdodCl9e2dfblxsZWZ0KDFcbGVmdHxXX3tpfVxyaWdodClccmlnaHQpfVwgLVwgXGZyYWN7SVxsZWZ0KEFfe2l9PTBccmlnaHQpfXtnX25cbGVmdCgwXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIFxyaWdodClcbGVmdFtZX3tpfS1cYmFye1F9X3tufV57MH1cbGVmdChBX3tpfSxXX3tpfVxyaWdodClccmlnaHRdK1xmcmFjezF9e259XHN1bV97aT0xfV57bn1cYmFye1F9X3tufV57MH1cbGVmdCgxLFwgV197aX1ccmlnaHQpLVxiYXJ7UX1fe259XnswfVxsZWZ0KDAsXCBXX3tpfVxyaWdodCkuICg0KSQkCgojU3RydWN0dXJhbCBjYXVzYWwgZnJhbWV3b3JrCgojI0RpcmVjdCBBY3ljbGljIEdyYXBoCiFbXShGaWd1cmVzL0RBRy5wbmcpCkZpZ3VyZSAxLiBEaXJlY3QgQWN5Y2xpYyBHcmFwaAoKI0NhdXNhbCBhc3N1bXB0aW9ucyAKVW5kZXIgdGhlIGNvdW50ZXJmYWN0dWFsIGZyYW1ld29yayB0aGUgZm9sbG93aW5nIGFzc3VtcHRpb25zIGhhdmUgdG8gYmUgY29uc2lkZXJlZCB0byBlc3RpbWF0ZSB0aGUgJFxwc2koUF97MH0pJCAoQVRFKSB3aXRoIGEgbW9uZGVsIGZvciAkUF97MH0kIGF1Z21lbnRlZCB3aXRoIGFkZGl0aW9uYWwgbm9udGVzdGF0YmxlIGN1YXNhbCBhc3N1bXB0aW9ucyBbQHJ1YmluMjAxMV0sIFtAdmFuMjAxMV06ICAgICAKCiMjQ01JIG9yIFJhbmRvbWl6YXRpb24gCigkWV97MH0sWV97MX1ccGVycCRBfFcpIG9mIHRoZSBiaW5hcnkgdHJlYXRtZW50IGVmZmVjdCAoQSkgb24gdGhlIG91dGNvbWUgKFkpIGdpdmVuIHRoZSBzZXQgb2Ygb2JzZXJ2ZWQgY292YXJpYXRlcyAoVyksIHdoZXJlIFcgPSAoVzEsICBXMiwgVzMsIOKApiAsIFdrKS4gCgojI1Bvc2l0aXZpdHkgCmEgz7UgQTogUChBPWEgfCBXKSA+IDAgIApQKEE9MXxXPXcpID4gMCBhbmQgUChBPTB8IFcgPSB3KSA+IDAgZm9yIGVhY2ggcG9zc2libGUgdy4gIAoKIyNDb25zaXN0ZW5jeSBvciBTVVRWQTogClRoZSBvYnNlcnZlZCBvdXRjb21lIHZhbHVlLCB1bmRlciB0aGUgb2JzZXJ2ZWQgdHJlYXRtZW50LCBpcyBlcXVhbCB0byB0aGUgY291bnRlcmZhY3R1YWwgb3V0Y29tZSBjb3JyZXNwb25kaW5nIHRvIHRoZSBvYnNlcnZlZCB0cmVhdG1lbnQgZm9yIGlkZW50aWNhbCBpbmRlcGVuZGVudCBkaXN0cmlidXRlZCAoaS5pLmQuKSB2YXJpYWJsZXMuICAgIAoKI1RNTEUgZmxvdyBjaGFydCAKKipTb3VyY2UqKjogTWFyayB2YW4gZGVyIExhYW4gYW5kIFNoZXJyaSBSb3NlLiBUYXJnZXRlZCBsZWFybmluZzogY2F1c2FsIGluZmVyZW5jZSBmb3Igb2JzZXJ2YXRpb25hbCBhbmQgZXhwZXJpbWVudGFsIGRhdGFTcHJpbmdlciBTZXJpZXMgaW4gU3RhdGlzdGljcywgMjAxMS4gIAohW10oRmlndXJlcy90bWxlLnBuZykKRmlndXJlIDIuIFRNTEUgZmxvdyBjaGFydCAoUm9hZCBtYXApCgojRGF0YSBnZW5lcmF0aW9uCgojI1NpbXVsYXRpb24gCgpJbiBSIHdlIGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHRoZSBkYXRhLiBUaGUgZnVuY3Rpb24gd2lsbCBoYXZlIGFzIGlucHV0IG51bWJlciBvZiBkcmF3cyBhbmQgYXMgb3V0cHV0IHRoZSBnZW5lcmF0ZWQgb2JzZXJ2ZWQgZGF0YSAoT2JzRGF0YSkgaW5jbHVkaW5nIHRoZSBjb3VudGVyZmFjdHVhbHMgKFkxLCBZMCkuICAKClRoZSBzaW11bGF0ZWQgZGF0YSByZXBsaWNhdGlvbmcgdGhlIERBRyBpbiBGaWd1cmUgMTogIAoKMS4gWTogbW9ydGFsaXR5IGJpbmFyeSBpbmRpY2F0b3IgKDEgZGVhdGgsIDAgYWxpdmUpIAoyLiBBOiBiaW5hcnkgdHJlYXRtZW50IGZvciBlbWVyZ2VuY3kgcHJlc2VudGF0aW9uIGF0IGNhbmNlciBkaWFnbm9zaXMgICgxIEVQLCAwIE5vbkVQKSAgICAKMy4gVzE6IEdlbmRlciAoMSBtYWxlOyAwIGZlbWFsZSkgIAo0LiBXMjogQWdlIGF0IGRpYWdub3NpcyAoMCA8NjU7IDEgPj02NSkgIAo0LiBXMzogQ2FuY2VyIFROTSBjbGFzc2lmaWNhdGlvbiAoc2NhbGUgZnJvbSAxIHRvIDQpICAKNS4gVzQ6IENvbW9yYmlkaXRpZXMgKHNjYWxlIGZyb20gMSB0byA1KSAgCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImJyb29tIikKb3B0aW9ucyhkaWdpdHM9NCkKZ2VuZXJhdGVEYXRhIDwtIGZ1bmN0aW9uKG4pewogIHcxIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9MC41KQogIHcyIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9MC42NSkKICB3MyA8LSByb3VuZChydW5pZihuLCBtaW49MCwgbWF4PTQpLCBkaWdpdHM9MykKICB3NCA8LSByb3VuZChydW5pZihuLCBtaW49MCwgbWF4PTUpLCBkaWdpdHM9MykKICBBICA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTAuNCArIDAuMip3MiArIDAuMTUqdzMgKyAwLjIqdzQgKyAwLjE1KncyKnc0KSkKICBZICA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTEgKyBBIC0wLjEqdzEgKyAwLjMqdzIgKyAwLjI1KnczICsgMC4yKnc0ICsgMC4xNSp3Mip3NCkpCiAgCiAgIyBjb3VudGVyZmFjdHVhbAogIFkuMSA8LSByYmlub20obiwgc2l6ZT0xLCBwcm9iPSBwbG9naXMoLTEgKyAxIC0wLjEqdzEgKyAwLjMqdzIgKyAwLjI1KnczICsgMC4yKnc0ICsgMC4xNSp3Mip3NCkpCiAgWS4wIDwtIHJiaW5vbShuLCBzaXplPTEsIHByb2I9IHBsb2dpcygtMSArIDAgLTAuMSp3MSArIDAuMyp3MiArIDAuMjUqdzMgKyAwLjIqdzQgKyAwLjE1KncyKnc0KSkKICAKICAjIHJldHVybiBkYXRhLmZyYW1lCiAgZGF0YS5mcmFtZSh3MSwgdzIsIHczLCB3NCwgQSwgWSwgWS4xLCBZLjApCn0Kc2V0LnNlZWQoNzc3NykKT2JzRGF0YSA8LSBnZW5lcmF0ZURhdGEobj0xMDAwMCkKVHJ1ZV9Qc2kgPC0gbWVhbihPYnNEYXRhJFkuMS1PYnNEYXRhJFkuMCk7CmNhdCgiIFRydWVfUHNpOiIsIFRydWVfUHNpKQpCaWFzX1BzaSA8LSBsbShkYXRhPU9ic0RhdGEsIFl+IEEpCmNhdCgiXG4iKQpjYXQoIlxuIE5haXZlX0JpYXNlZF9Qc2k6IixzdW1tYXJ5KEJpYXNfUHNpKSRjb2VmWzIsIDFdKQpOYWl2ZV9CaWFzIDwtICgoc3VtbWFyeShCaWFzX1BzaSkkY29lZlsyLCAxXSktVHJ1ZV9Qc2kpOyBjYXQoIlxuIE5haXZlcyBiaWFzOiIsIE5haXZlX0JpYXMpCk5haXZlX1JlbGF0aXZlX0JpYXMgPC0gKCgoc3VtbWFyeShCaWFzX1BzaSkkY29lZlsyLCAxXSktVHJ1ZV9Qc2kpL1RydWVfUHNpKSoxMDA7IGNhdCgiXG4gUmVsYXRpdmUgTmFpdmVzIGJpYXM6IiwgTmFpdmVfUmVsYXRpdmVfQmlhcywiJSIpCmBgYAojI0RhdGEgdmlzdWFsaXphdGlvbgoKYGBge3J9CiMgRFQgdGFibGUgPSBpbnRlcmFjdGl2ZQojIGluc3RhbGwucGFja2FnZXMoIkRUIikgIyBpbnN0YWxsIERUIGZpcnN0CmxpYnJhcnkoRFQpCmRhdGF0YWJsZShoZWFkKE9ic0RhdGEsIG4gPSBucm93KE9ic0RhdGEpKSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIGRpZ2l0cyA9IDIpKQpgYGAKCiNUTUxFIHNpbXBsZSBpbXBsZW1lbnRhdGlvbgoKIyNTdGVwIDE6ICRRX3swfSQoQSwqKlcqKikgCkVzdGltYXRpb24gb2YgdGhlIGluaXRpYWwgcHJvYmFiaWxpdHkgb2YgdGhlIG91dGNvbWUgKFkpIGdpdmVuIHRoZSB0cmVhdG1lbnQgKEEpIGFuZCB0aGUgc2V0IG9mIGNvdmFyaWF0ZXMgKFcpLCBkZW5vdGVkIGFzIHRoZSAkUV97MH0kKEEsKipXKiopLiBUbyBlc3RpbWF0ZSAkUV97MH0kKEEsKipXKiopIHdlIGNhbiB1c2UgYSBzdGFuZGFyZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsOiAKCiQkXHRleHR7bG9naXR9W1AoWT0xfEEsVyldXCw9XCxcYmV0YV97MH1cLCtcLFxiZXRhX3sxfUFcLCtcLFxoYXR7XGJldGFfezJ9XntUfX1XLiQkIAoKVGhlcmVmb3JlLCB3ZSBjYW4gZXN0aW1hdGUgdGhlIGluaXRpYWwgcHJvYmFiaWxpdHkgYXMgZm9sbG93czogCgokJFxiYXJ7UX1eezB9KEEsVylcLD1cLFx0ZXh0e2V4cGl0fShcaGF0e1xiZXRhX3swfX1cLCtcLFxoYXR7XGJldGFfezF9fUFcLCtcLFxoYXR7XGJldGFfezJ9XntUfX1XKS4kJCAKClRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkgY2FuIGJlIGVzdGltYXRlZCB1c2luZyB0aGUgU3VwZXIgTGVhcm5lciBsaWJyYXJ5IGltcGxlbWVudGVkIGluIHRoZSBSIHBhY2thZ2Ug4oCcU3VwZXItTGVhcm5lcuKAnSAoUmVmKSB0byBpbmNsdWRlIGFueSB0ZXJtcyB0aGF0IGFyZSBmdW5jdGlvbnMgb2YgQSBvciBXIChlLmcuLCBwb2x5bm9taWFsIHRlcm1zIG9mIEEgYW5kIFcsIGFzIHdlbGwgYXMgdGhlIGludGVyYWN0aW9uIHRlcm1zIG9mIEEgYW5kIFcsIGNhbiBiZSBjb25zaWRlcmVkKS4gQ29uc2VxdWVudGx5LCBmb3IgZWFjaCBzdWJqZWN0LCB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZm9yIGJvdGggcG90ZW50aWFsIG91dGNvbWVzICRcYmFye1F9XnswfSgwLFcpJCBhbmQgICRcYmFye1F9XnswfSgxLFcpJCBjYW4gYmUgZXN0aW1hdGVkIGJ5IHNldHRpbmcgQSA9IDAgYW5kIEEgPSAxIGZvciBldmVyeW9uZSByZXNwZWN0aXZlbHk6CiQkXGJhcntRfV57MH0oMCxXKVwsPVwsXHRleHR7ZXhwaXR9KFxoYXR7XGJldGFfezB9fVwsK1wsXGhhdHtcYmV0YV97Mn1ee1R9fVcpLCQkCmFuZCwgIAokJFxiYXJ7UX1eezB9KDEsVylcLD1cLFx0ZXh0e2V4cGl0fShcaGF0e1xiZXRhX3swfX1cLCtcLFxoYXR7XGJldGFfezF9fUFcLCtcLFxoYXR7XGJldGFfezJ9XntUfX1XKS4kJAoKYGBge3J9Ck9ic0RhdGEgPC1zdWJzZXQoT2JzRGF0YSwgc2VsZWN0PWModzEsdzIsdzMsdzQsQSxZKSkKWSAgPC0gT2JzRGF0YSRZCkEgIDwtIE9ic0RhdGEkQQp3MSA8LSBPYnNEYXRhJHcxCncyIDwtIE9ic0RhdGEkdzIKdzMgPC0gT2JzRGF0YSR3Mwp3NCA8LSBPYnNEYXRhJHc0Cm0gIDwtIGdsbShZIH4gQSArIHcxICsgdzIgKyB3MyArIHc0LCBmYW1pbHk9Ymlub21pYWwsIGRhdGE9T2JzRGF0YSkKUSAgPC0gY2JpbmQoUUFXID0gcHJlZGljdChtKSwKICAgICAgICAgICAgUTFXID0gcHJlZGljdChtLCBuZXdkYXRhPWRhdGEuZnJhbWUoQSA9IDEsIHcxLCB3MiwgdzMsIHc0KSksCiAgICAgICAgICAgIFEwVyA9IHByZWRpY3QobSwgbmV3ZGF0YT1kYXRhLmZyYW1lKEEgPSAwLCB3MSwgdzIsIHczLCB3NCkpKQpRMCA8LSBhcy5kYXRhLmZyYW1lKFEpO21lYW4oUTAkUTFXLVEwJFEwVykKYGBgCgojI1N0ZXAgMjogJGdfezB9KEEsVykkCkVzdGltYXRpb24gb2YgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSB0cmVhdG1lbnQgKEEpIGdpdmVuIHRoZSBzZXQgb2YgY292YXJpYXRlcyAoVyksIGRlbm90ZWQgYXMgJGdfezB9KEEsVykkLiBXZSBjYW4gdXNlIGFnYWluIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCBhbmQgdG8gaW1wcm92ZSB0aGUgcHJlZGljdGlvbiBhbGdvcml0aG0gd2UgY2FuIHVzZSB0aGUgU3VwZXIgTGVhcm5lciBsaWJyYXJ5IG9yIGFueSBvdGhlciBtYWNoaW5lIGxlYXJuaW5nIGVzdHJhdGVneTogIAoKJCRcdGV4dHtsb2dpdH1bUChBPTF8VyldXCw9XCxcYmV0YV97MH1cLCtcLFxiZXRhX3sxfV57VH1XLiQkIApUaGVuLCB3ZSBlc3RpbWF0ZSB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXR5IG9mIFAoQXxXKSA9ICRcaGF0e2d9KDEsVykkIHVzaW5nOiAgCgokJFxoYXR7Z30oMSxXKVwsPVwsXHRleHR7ZXhwaXR9XCw9XCwoXGhhdHtcYmV0YV97MH19XCwrXCxcaGF0e1xiZXRhX3syfV57VH19VykuJCQgCgpgYGB7cn0KZyA8LSBnbG0oQSB+IHcyICsgdzMgKyB3NCwgZmFtaWx5ID0gYmlub21pYWwpCmcxdyA9IHByZWRpY3QoZywgdHlwZSA9InJlc3BvbnNlIik7c3VtbWFyeShnMXcpCmBgYAojI1N0ZXAgMzogSEFXIGFuZCAkXGVwc2lsb24kClRoaXMgc3RlcCBhaW1zIHRvIGZpbmQgYSBiZXR0ZXIgcHJlZGljdGlvbiBtb2RlbCB0YXJnZXRlZCBhdCBtaW5pbWlzaW5nIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKSBmb3IgdGhlIHBvdGVudGlhbCBvdXRjb21lcyBieSB1c2luZyB0aGUgc28tY2FsbGVkIGVmZmljaWVudCBJQyBlc3RpbWF0aW9uIGVxdWF0aW9uLiBGb3IgdGhlIEFURSBvbiBzdGVwIGNvbnZlcmdlbmNlIGlzIGd1YXJhbnRlZWQgZ2l2ZW4gJFxiYXJ7UX1eezB9JCBhbmQgJFxoYXR7Z30oMSxXKSQgdGhlIGZsdWN0dWF0aW5nIHBhcmFtZXRlciBpcyBtb2RlbGxlZCB1c2luZyBhIHBhcmFtZXRyaWMgd29ya2luZyBtb2RlbCB0byBlc3RpbWF0ZSB0aGUgZmx1Y3R1YXRpb24gcGFyYW1ldGVycyAkXGVwc2lsb25fezB9JCBhbmQgJFxlcHNpbG9uX3sxfSQgYXMgZm9sbG93czoKCiQkXGJhcntRXnsxfX0oQSxXKVwsPVwsXHRleHR7ZXhwaXR9XGxlZnRbXHRleHR7bG9naXR9XGxlZnQoXGJhcntRXnswfX0oQSwgVylccmlnaHQpXCwrXCxcaGF0e1xlcHNpbG9uX3swfX1IX3swfShBLFcpXCwrXCxcaGF0e1xlcHNpbG9uX3sxfX1IX3sxfShBLFcpXHJpZ2h0XSAgKDUpJCQKJCRcYmFye1FeezF9fSgwLFcpXCw9XCxcdGV4dHtleHBpdH1cbGVmdFtcdGV4dHtsb2dpdH1cbGVmdChcYmFye1FeezB9fShBLFcpXHJpZ2h0KVwsK1wsXGhhdHtcZXBzaWxvbl97MH19SF97MH0oQSxXKVxyaWdodF0kJAoKCiQkXGJhcntRXnsxfX0oMSxXKVwsPVwsXHRleHR7ZXhwaXR9XGxlZnRbXHRleHR7bG9naXR9XGxlZnQoXGJhcntRXnswfX0oQSxXKVxyaWdodClcLCtcLFxoYXR7XGVwc2lsb25fezF9fUhfezF9KEEsVylccmlnaHRdJCQKV2hlcmUsCiQkSF97MH0oQSxXKVwsPVwsXGZyYWN7SShBPTApfXtcaGF0e2d9KDB8Vyl9XDtcdGV4dHthbmR9LFw7SF97MX0oQSxXKVwsPVwsXGZyYWN7SShBPTEpfXtcaGF0e2d9KDF8Vyl9JCQgYXJlIHJlZmVycmVkIHRvIGFzIGNsZXZlciBjb3ZhcmlhdGVzIChub3RlIHRoYXQgJFxoYXR7Z30oQXxXKSQgaXMgZXN0aW10ZWQgZnJvbSBzdGVwIDIpLgoKVGhlIGZsdWN0dWF0aW9uIHBhcmFtZXRlcnMgJChcaGF0e1xlcHNpbG9ufV97MH1cLCxcLFxoYXR7XGVwc2lsb259X3sxfSkkIGFyZSBlc3RpbWF0ZWQgdXNpbmcgbWF4aW11bSBsaWtlbGlob29kIHByb2NlZHVyZXMgYnkgc2V0dGluZyAkXHRleHR7bG9naXR9KFxiYXJ7UV57MH19KEEsVykpJCBhcyBhbiBvZmZzZXQgaW4gYSBpbnRlcmNlcHQtZnJlZSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggJEhfezB9JCBhbmQgJEhfezF9JCBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIFRoZW4sIHRoZSBlc3RpbWF0ZWQgcHJvYmFiaWxpdHkgb2YgdGhlIHBvdGVudGlhbCBvdXRjb21lcyBpcyB1cGRhdGVkIGJ5IHRoZSBzdWJzdGl0dXRpb24gcGFyYW1ldGVycyAkKFxoYXR7XGVwc2lsb25fezB9fVwsLFwsXGhhdHtcZXBzaWxvbl97MX19KSQuIFRoZSBzdWJzdGl0dXRpb24gdXBkYXRlIGlzIHBlcmZvcm1lZCBieSBzZXR0aW5nIEEgPSAwIGFuZCBBID0gMSBmb3IgZWFjaCBzdWJqZWN0IGluIHRoZSBpbml0aWFsIGVzdGltYXRlIHByb2JhYmlsaXR5IG9mIHRoZSBwb3RlbnRpYWwgb3V0Y29tZXMgJFxiYXJ7UV57MX19KDAsVylcLCxcLFxiYXJ7UV57MX19KDEsVykkLCBhcyB3ZWxsIGFzIGluIHRoZSBjbGV2ZXIgY292YXJpYXRlcyAkSF97MH0oMCxXKVw7XHRleHR7YW5kfVw7IEhfezF9KDEsVykkLiAKCkZvciB0aGUgQVRFLCB0aGUgdXBkYXRlZCBlc3RpbWF0ZSBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIG9ubHkgbmVlZHMgb25lIGl0ZXJhdGlvbiAkXFBzaShcYmFye1Ffe259fV57Kn0pJCBmcm9tICRcYmFye1F9XnswfShBLFcpXCw9PlxiYXJ7UV57MX19KEEsVykkLiBUaGVyZWZvcmUsIG1vZGVsICg1KSB0YXJnZXRzICRFW1xoYXR7WX1fe0E9MH1dXDtcdGV4dHthbmR9XDsgRVtcaGF0e1l9X3tBPTF9XSQgc2ltdWx0YW5lb3VzbHkgYnkgaW5jbHVkaW5nIGJvdGggJEhfezB9KEEsVylcLFx0ZXh0e2FuZH1cLEhfezF9KEEsVykkIGluIHRoZSBtb2RlbC4gICAKCmBgYHtyfQojTW9kZWwgNTogQ2xldmVyIGNvdmFyaWF0ZSBhbmQgZmx1Y3R1YXRpbmcvc3Vic3RpdHV0aW9uIHBhcmFtdGVyZXMKaCA8LSBjYmluZChBL2cxdyAtKDEtQSkvKDEtZzF3KSwgMS9nMXcsIC0xLygxLWcxdykpCmVwc2lsb24gPC0gY29lZihnbG0oWSB+IC0xICsgaFssMV0gKyBvZmZzZXQoUVssIlFBVyJdKSwgZmFtaWx5ID0gYmlub21pYWwpKTtlcHNpbG9uCmBgYAoKIyNTdGVwIDQ6ICRcYmFye1Ffe259fV57Kn0kCiQkXHBzaSBUTUxFLG4gPSBcUHNpKFFfe259XnsqfSk9IHtcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGJhcntRfV97bn1eezF9XGxlZnQoMSxcIFdfe2l9XHJpZ2h0KS1cYmFye1F9X3tufV57MX1cbGVmdCgwLFwgV197aX1ccmlnaHQpfS4gICgxKSQkCmBgYHtyfQpRc3RhciA8LSBwbG9naXMoUSArIGVwc2lsb24qaCkKUHNpIDwtIG1lYW4oUXN0YXJbLCJRMVciXSAtIFFzdGFyWywiUTBXIl0pO2NhdCgiVE1MRV9Qc2k6IiwgUHNpKQpjYXQoIlxuIFRNTEUuU0lfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktUHNpKSkKY2F0KCJcbiBSZWxhdGl2ZV9UTUxFLlNJX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktUHNpKS9UcnVlX1BzaSoxMDAsIiUiKQpgYGAKIyNTdGVwIDU6IEluZmVyZW5jZQokJElDX3tufShPX3tpfSlcIFwgPVwgXGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezF9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcYmFye1F9X3tufV57MX1cbGVmdCgxLFwgV197aX1ccmlnaHQpLVxiYXJ7UX1fe259XnsxfVxsZWZ0KDAsXCBXX3tpfVxyaWdodCkgLSBccHNpIFRNTEUsbi4gKDIpJCQKCndoZXJlIHRoZSB2YXJpYW5jZSBvZiB0aGUgQVRFOiAgCgokJFxzaWdtYSh7XHBzaV97MH19KT1cc3FydHtcZnJhY3tWYXIoSUNfe259KX17bn19LiAgKDMpJCQKCmBgYHtyfQpRIDwtIGFzLmRhdGEuZnJhbWUoUSkKSUMgPC0gaFssMV0qKFktUSRRQVcpICsgUSRRMVcgLSBRJFEwVyAtIFBzaTtzdW1tYXJ5KElDKQpuIDwtIG5yb3coT2JzRGF0YSkKdmFySGF0LklDIDwtIHZhcihJQykvbjsgdmFySGF0LklDCiNQc2kgYW5kIDk1JUNJIGZvciBQc2kKY2F0KCJcbiBUTUxFLlNJX2JpYXM6IiwgYWJzKFRydWVfUHNpLVBzaSkpCmNhdCgiXG4gOTUlQ0k6IiwgYyhQc2ktMS45NipzcXJ0KHZhckhhdC5JQyksICBQc2krMS45NipzcXJ0KHZhckhhdC5JQykpKQpjYXQoIlxuIFJlbGF0aXZlX1RNTEUuU0lfYmlhczoiLGFicyhUcnVlX1BzaS1Qc2kpL1RydWVfUHNpKjEwMCwiJSIpCmBgYAoKI0NvbXBhcmlzb24gd2l0aCBBSVBUVwoKJCRccHNpX3swfV57QUlQVFctQVRFfVwgXCA9XCBcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGxlZnQoXGZyYWN7SVxsZWZ0KEFfe2l9PTFccmlnaHQpfXtnX25cbGVmdCgxXGxlZnR8V197aX1ccmlnaHQpXHJpZ2h0KX1cIC1cIFxmcmFje0lcbGVmdChBX3tpfT0wXHJpZ2h0KX17Z19uXGxlZnQoMFxsZWZ0fFdfe2l9XHJpZ2h0KVxyaWdodCl9XCBccmlnaHQpXGxlZnRbWV97aX0tXGJhcntRfV97bn1eezB9XGxlZnQoQV97aX0sV197aX1ccmlnaHQpXHJpZ2h0XStcZnJhY3sxfXtufVxzdW1fe2k9MX1ee259XGJhcntRfV97bn1eezB9XGxlZnQoMSxcIFdfe2l9XHJpZ2h0KS1cYmFye1F9X3tufV57MH1cbGVmdCgwLFwgV197aX1ccmlnaHQpLiAoNCkkJAoKYGBge3J9CkFJUFRXIDwtIG1lYW4oKGhbLDFdKihZLVEkUUFXKSkrKFEkUTFXLVEkUTBXKSk7IEFJUFRXCmNhdCgiXG4gQUlQVFdfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktQUlQVFcpKQpjYXQoIlxuIFJlbGF0aXZlX0FJUFRXX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktQUlQVFcpL1RydWVfUHNpKjEwMCwiJSIpCmBgYAoKI1RNTEUgdXNpbmcgdGhlIFN1cGVyLUxlYXJuZXIgIAoKYGBge3J9CiNRMApsaWJyYXJ5KFN1cGVyTGVhcm5lcikKI1NwZWNpZnkgU3VwZXJMZWFybmVyIGxpYnJhcmllcwpTTC5saWJyYXJ5IDwtIGMoIlNMLmdsbSIsIlNMLnN0ZXAiLCJTTC5nbG0uaW50ZXJhY3Rpb24iKQojRGF0YSBmcmFtZSB3aXRoIFggd2l0aCBiYXNlbGluZSBjb3ZhcmlhdGVzIGFuZCBleHBvc3VyZQpYIDwtIHN1YnNldChPYnNEYXRhLCBzZWxlY3Q9YyhBLCB3MSwgdzIsIHczLCB3NCkpCm4gPC0gbnJvdyhPYnNEYXRhKQojQ3JlYXRlIGRhdGEgZnJhbWVzIHdpdGggQT0xIGFuZCBBPTAKWDE8LVgwPC1YClgxJEEgPC0xClgwJEEgPC0wCiNDcmVhdGUgbmV3IGRhdGEgYnkgc3RhY2tpbmcKbmV3ZGF0YSA8LSByYmluZChYLFgxLFgwKQojQ2FsbCBzdXBlcmxlYXJuZXIKUWluaXQgPC0gU3VwZXJMZWFybmVyKFk9T2JzRGF0YSRZLCBYPVgsIG5ld1g9bmV3ZGF0YSwgU0wubGlicmFyeT1TTC5saWJyYXJ5LCBmYW1pbHk9ImJpbm9taWFsIikKUWluaXQKI1ByZWRpY3Rpb25zCiNQcmVkIHByb2Igb2Ygc3Vydml2YWwgZ2l2ZW4gQSwgVwpRYmFyQVcgPC0gUWluaXQkU0wucHJlZGljdFsxOm5dCiNQcmVkIHByb2Igb2Ygc3VydiBmb3IgZWFjaCBzdWJqZWN0IGdpdmVuIEE9MSBhbmQgdwpRYmFyMVcgPC0gUWluaXQkU0wucHJlZGljdFsobisxKTooMipuKV0KI1ByZWQgcHJvYiBvZiBzdXJ2IGZvciBlYWNoIHN1YmplY3QgZ2l2ZW4gQT0wIGFuZCB3ClFiYXIwVyA8LSBRaW5pdCRTTC5wcmVkaWN0WygyKm4rMSk6KDMqbildCiNTaW1wbGUgc3Vic3RpdHV0aW9uIGVzdGltYXRvciBQc2koUTApClBzaUhhdC5TUyA8LSBtZWFuKFFiYXIxVy1RYmFyMFcpO1BzaUhhdC5TUwpgYGAKCmBgYHtyfQojU3RlcCAyIGdfMChBfFcpIHdpdGggU3VwZXJMZWFybmVyCncgPC0gc3Vic2V0KE9ic0RhdGEsIHNlbGVjdD1jKHcxLHcyLHczLHc0KSkKZ0hhdFNMIDwtIFN1cGVyTGVhcm5lcihZPU9ic0RhdGEkQSwgWD13LCBTTC5saWJyYXJ5PVNMLmxpYnJhcnksIGZhbWlseSA9IGJpbm9taWFsKQpnSGF0U0w7bWVhbihnSGF0U0wpCiNHZW5lcmF0ZSB0aGUgcHJlZCBwcm9iIG9mIEE9MSBhbmQsIEE9MCBnaXZlbiBjb3ZhcmlhdGVzCmdIYXQxVyA8LSBnSGF0U0wkU0wucHJlZGljdApnSGF0MFcgPC0gMS1nSGF0MVcKI1N0ZXAgMzogQ2xldmVyIGNvdmFyaWF0ZQpIQVcgPC0gYXMubnVtZXJpYyhPYnNEYXRhJEE9PTEpL2dIYXQxVyAtIGFzLm51bWVyaWMoT2JzRGF0YSRBPT0wKS9nSGF0MFc7bWVhbihIQVcpCkgxVyA8LSAgMS9nSGF0MVcKSDBXIDwtIC0xL2dIYXQwVwpgYGAKCmBgYHtyfQojU3RlcCA0OiBTdWJzdGl0dXRpb24gZXN0aW1haXRvbiBRKiBvZiB0aGUgQVRFLgpsb2dpdFVwZGF0ZSA8LSBnbG0oT2JzRGF0YSRZIH4gLTEgKyBvZmZzZXQocWxvZ2lzKFFiYXJBVykpK0hBVywgZmFtaWx5PSdiaW5vbWlhbCcpCmVwcyA8LSBsb2dpdFVwZGF0ZSRjb2VmO2VwcwojQ2FsY3VsYXRpbmcgdGhlIHByZWRpY3RlZCB2YWx1ZXMgZm9yIGVhY2ggc3ViamVjdCB1bmRlciBlYWNoIHR4dApRYmFyQVcuc3RhciA8LSBwbG9naXMocWxvZ2lzKFFiYXJBVykrZXBzKkhBVykKUWJhcjFXLnN0YXIgPC0gcGxvZ2lzKHFsb2dpcyhRYmFyMVcpK2VwcypIMVcpClFiYXIwVy5zdGFyIDwtIHBsb2dpcyhxbG9naXMoUWJhcjBXKStlcHMqSDBXKQpQc2lIYXQuVE1MRS5TTCA8LSBtZWFuKFFiYXIxVy5zdGFyKSAtIG1lYW4oUWJhcjBXLnN0YXIpCmNhdCgiUHNpSGF0LlRNTEUuU0w6IiwgUHNpSGF0LlRNTEUuU0wpCmNhdCgiXG4gUHNpSGF0LlRNTEUuU0xfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktUHNpSGF0LlRNTEUuU0wpKQpjYXQoIlxuIFJlbGF0aXZlX1BzaUhhdC5UTUxFLlNMX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktUHNpSGF0LlRNTEUuU0wpL1RydWVfUHNpKjEwMCwiJSIpCmBgYAoKI1ItVE1MRQoKYGBge3J9CmxpYnJhcnkodG1sZSkKdyA8LSBzdWJzZXQoT2JzRGF0YSwgc2VsZWN0PWModzEsdzIsdzMsdzQpKQp0bWxlIDwtIHRtbGUoWSwgQSwgVz13KQpjYXQoIlRNTEVSX1BzaToiLCB0bWxlJGVzdGltYXRlc1tbMl1dW1sxXV0sIjsiLCI5NSVDSSgiLCB0bWxlJGVzdGltYXRlc1tbMl1dW1szXV0sIikiKQpjYXQoIlxuIFRNTEVfYmlhczoiLCBhYnMoVHJ1ZV9Qc2ktdG1sZSRlc3RpbWF0ZXNbWzJdXVtbMV1dKSkKY2F0KCJcbiBSZWxhdGl2ZV9UTUxFX2JpYXM6IixhYnMoVHJ1ZV9Qc2ktdG1sZSRlc3RpbWF0ZXNbWzJdXVtbMV1dKS9UcnVlX1BzaSoxMDAsIiUiKQpgYGAKCiNSLVRNTEUgaW1wcm92aW5nIHByZWRpY3Rpb24KCmBgYHtyfQpTTC5UTUxFUi5Qc2kgPC0gdG1sZShZPVksIEE9QSwgVz13LCBmYW1pbHk9ImJpbm9taWFsIiwgCiAgICBRLlNMLmxpYnJhcnkgPSBjKCJTTC5nbG0iLCAiU0wuZ2xtLmludGVyYWN0aW9uIiwgIlNMLmdhbSIsICJTTC5yYW5kb21Gb3Jlc3QiKSwKICAgIGcuU0wubGlicmFyeSA9IGMoIlNMLmdsbSIsICJTTC5nbG0uaW50ZXJhY3Rpb24iLCAiU0wuZ2FtIiwgIlNMLnJhbmRvbUZvcmVzdCIpKQoKY2F0KCJTTC5UTUxFUi5Qc2k6IiwgU0wuVE1MRVIuUHNpJGVzdGltYXRlc1tbMl1dW1sxXV0sIjsiLCI5NSVDSSgiLCBTTC5UTUxFUi5Qc2kkZXN0aW1hdGVzW1syXV1bWzNdXSwiKSIpCmNhdCgiXG4gU0wuVE1MRVIuUHNpX2JpYXM6IiwgYWJzKFRydWVfUHNpLVNMLlRNTEVSLlBzaSRlc3RpbWF0ZXNbWzJdXVtbMV1dKSkKY2F0KCJcbiBSZWxhdGl2ZV9TTC5UTUxFUi5Qc2lfYmlhczoiLGFicyhUcnVlX1BzaS1TTC5UTUxFUi5Qc2kkZXN0aW1hdGVzW1syXV1bWzFdXSkvVHJ1ZV9Qc2kqMTAwLCIlIikKYGBgCgojVGhhbmsgeW91ICAKVGhhbmsgeW91IGZvciBwYXJ0aWNpcGF0aW5nIGluIHRoaXMgdHV0b3JpYWwuICAKSWYgeW91IGhhdmUgdXBkYXRlcyBvciBjaGFuZ2VzIHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gbWFrZSwgcGxlYXNlIHNlbmQgPGEgaHJlZj0iaHR0cHM6Ly9naXRodWIuY29tL21pZ2FyaWFuZS9NQUxGIiB0YXJnZXQ9Il9ibGFuayI+bWU8L2E+IGEgcHVsbCByZXF1ZXN0LgpBbHRlcm5hdGl2ZWx5LCBpZiB5b3UgaGF2ZSBhbnkgcXVlc3Rpb25zLCBwbGVhc2UgZS1tYWlsIG1lLiAgCioqTWlndWVsIEFuZ2VsIEx1cXVlIEZlcm5hbmRleioqICAKKipFLW1haWw6KiogKm1pZ3VlbC1hbmdlbC5sdXF1ZSBhdCBsc2h0bS5hYy51ayogIAoqKlR3aXR0ZXIqKiBgQFdBVFpJTEVJYCAgCgojIFNlc3Npb24gSW5mbyAKYGBge3Igc2Vzc2lvbi1pbmZvLCByZXN1bHRzID0nbWFya3VwJ30KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYAojIFJlZmVyZW5jZXMgCg==